// System includes
#include <unistd.h>      // For sleep
#include <pthread.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>

// Other includes
#include "Types.hpp"
#include "CSocketReader.hpp"
#include "CNRFReader.hpp"
#include "CTrace.hpp"
#include "CCommand.hpp"
#include "CCommandsDispatcher.hpp"

void printUsage(const char* runCommand);   // Forward declaration

void* socketReaderThreadStart(void* data);
void* arduinoReaderThreadStart(void* data);

void sigIntHandler(int);

int main(int argc,char** argv)
{
   signal(SIGINT, &sigIntHandler);  //If program terminates go to assigned function "myFunction".

   if (1 == argc)
   {
      printUsage(argv[0]);
   }

   int paramPort = PORT;
   char paramSerialDeviceName[128]; strcpy(paramSerialDeviceName, SERIAL_DEVICE_NAME);

   // Parsing parameters provided from command line
   // If something is parsed scuuessfully, overriding default values
   {
      if (1 < argc)
      {
         for (int i=0; i<argc; i++)
         {
            if (0 == strcmp(argv[i], "--device"))
            {
               if (((i+1) < argc) && (0 != argv[i+1]))   // nect item exists in array and not NULL
               {
                  strcpy(paramSerialDeviceName, argv[i+1]);
                  TRC_DEBUG("Parsed new device name = %s", paramSerialDeviceName);
               }
               else
               {
                  TRC_ERROR("Not correct device name. Using default value");
               }
            }
            else if (0 == strcmp(argv[i], "--port"))
            {
               if (((i+1) < argc) && (0 != argv[i+1]))   // nect item exists in array and not NULL
               {
                  paramPort = atoi(argv[i+1]);
                  TRC_DEBUG("Parsed new port = %d", paramPort);
               }
               else
               {
                  TRC_ERROR("Not correct port. Using default value");
               }
            }
         }
      }
   }

   // Starting reader threads
   // Configuring dispacher
   {
      pthread_t socketReaderThread;
      pthread_t arduinoReaderThread;

      CCommandsDispatcher* pCommandsDispatcher = CCommandsDispatcher::getInstance();
      CSocketReader socketReader(paramPort, pCommandsDispatcher);
    //CSerialReader arduinoReader(paramSerialDeviceName, pCommandsDispatcher);
      CNRFReader    arduinoReader(pCommandsDispatcher);

      pCommandsDispatcher->setReaderPointers(&socketReader, &arduinoReader);

      int socketReaderThreadRet = pthread_create(&socketReaderThread, NULL, socketReaderThreadStart, reinterpret_cast<void*>(&socketReader));
      int arduinoReaderThreadRet = pthread_create(&arduinoReaderThread, NULL, arduinoReaderThreadStart, reinterpret_cast<void*>(&arduinoReader));

      pthread_join(socketReaderThread, NULL);
   }
}

void printUsage(const char* runCommand)
{
   printf("===============================================================================================================\n");
   printf("This app acts like a bridge between Client applications and Arduino\n");
   printf("At startup it connects to Arduino via serial port '%s' \nand starts listening for TCP connection via port %d\n", SERIAL_DEVICE_NAME, PORT);
   printf("All commands got from Arduino it sends to Socket (if available)\n");
   printf("All commands got from Socket it sends to Arduino (assuming, Arduino is always connected)\n");
   printf("IMPORTANT. Please note, you may need to run this program from root\n");
   printf("Usage:\n");
   printf("   %s [OPTIONS]\n", runCommand);
   printf("Where OPTIONS could be:\n");
   printf("   --port <port number> - Defines custom port number. Default value is %d\n", PORT);
   printf("   --device <device name> - Defines custom device name. Default value is %s\n", SERIAL_DEVICE_NAME);
   printf("===============================================================================================================\n");
}

void* socketReaderThreadStart(void* pData)
{
   if (0 != pData)
   {
      CSocketReader* pReader = reinterpret_cast<CSocketReader*>(pData);
      pReader->start();
   }
}

void* arduinoReaderThreadStart(void* pData)
{
   if (0 != pData)
   {
      CNRFReader* pReader = reinterpret_cast<CNRFReader*>(pData);
      pReader->start();
   }
}

void sigIntHandler(int signal)
{
   TRC_WARNING("SIG INT catched");
   exit(0);
}